#include "DataStorage.h"

#include <string.h>


DataStorage::DataStorage()
{
	umbrellaPositions[0] = Point(44.25,  15);
	umbrellaPositions[1] = Point(44.25, -15);
	umbrellaPositions[2] = Point(36,  7.5);
	umbrellaPositions[3] = Point(36, -7.5);

	for(int i = 0; i < BALL_HISTORY_COUNT; i++)
	{
		ballStates[i] = NULL;
	}

	actualBallStateIndex = -1;

	cornerKickModeDuration = 0;
	freeKickModeDuration = 0;
	indFreeKickModeDuration = 0;
	kickInModeDuration = 0;

	scanBeforeKickOffAngle = 0;

	lfDribbleX = world->me->pos.GetX();
	lfDribbleY = world->me->pos.GetY();

	ballKickable = false;

#ifdef GOALIE
	playerRole = PR_Goalie;
	goalieCatch.goalieCatchBan = 0;
	goalieKickOff = GOALIE_GOFORBALL;
#else
	playerRole = PR_Player;
#endif

	turnNeck.futurePosition.SetX(0), turnNeck.futurePosition.SetY(0);
	turnNeck.futureMyPosition.SetX(0), turnNeck.futureMyPosition.SetY(0);
	turnNeck.nextTurn = 0;
	turnNeck.neckDirection = -1;
	turnNeck.avoidNextCorrection = false;
	turnNeck.changeView = 0;
}

DataStorage::DataStorage(const DataStorage& ib)
{
}


//vracia odkaz na instanciu tejto triedy
DataStorage& DataStorage::getInstance()
{
	static DataStorage instance;
	return instance;
}


// Vypocita a vrati vzdialenost k branke
float DataStorage::DistanceToGoal(const Point& pos) 
{
	const float posX = 52.5f;

	if (pos.GetY() < -world->params->server->goal_width / 2)
	{
		const Point UpperGoal(posX, -world->params->server->goal_width / 2);
		return (UpperGoal - pos).GetLength();
	}

	if (pos.GetY() > world->params->server->goal_width / 2) {
		const Point LowerGoal(posX, world->params->server->goal_width / 2);
		return (LowerGoal - pos).GetLength();
	}

	return fabsf(posX - pos.GetX());
}

// Vrati pointer na superovho brankara. Vrati NULL, ak superov brankar nie je v hracovom svete
Player* DataStorage::GetEnemyGoalie() 
{
	static Player *enemyGoalie;
	static int i;
	
	enemyGoalie = NULL;
	for(i = 0; i < world->enemies.GetSize(); i++)
	{
		if(world->enemies[i].IsGoalie()) 
		{
			enemyGoalie = &(world->enemies[i]);
			break;
		}
	}
	return enemyGoalie;
}

// vrati ukazovatel na nasho brankara, ak o nom nevieme, vrati sa NULL
Player* DataStorage::GetOurGoalie() 
{
	for(int i=0;i<world->friends.GetSize();i++)
	{
		if(world->friends[i].IsGoalie())
			return &world->friends[i];
	}
	return NULL;
}


// Vypocita sa vzdialenost k najblizsiemu superovi
// target -> cielovy referencny bod
// Vrati sa vzdialenost
float DataStorage::GetNearestEnemyDistance(const Point& target)
{
  
	float fMinDistance = 1000.0;
	
	for(int i = 0; i < world->enemies.GetSize(); i++)
	{
	  float fDiffEnemy = (world->enemies[i].pos - target).GetLength();
    
	  if (fDiffEnemy < fMinDistance)
		fMinDistance = fDiffEnemy;
    }

	return fMinDistance;
}

// Najde najblizsieho spoluhraca
// target -> cielovy referencny bod
// Vrati sa najblizsi index hraca, -1 ak ziadny
int DataStorage::GetNearestFriend(const Point& target)
{
	float min = 1000.0;
	int player = -1;

	for(int i = 0; i < world->friends.GetSize(); i++)
	{
		float diff = (world->friends[i].pos - target).GetLength();
		if(diff < min)
		{
			min = diff;
			player = i;
		}
	}

	return player;
}

// Vypocita vzdialenost k najblizsiemu spoluhracovi
// target -> Cielovy referencny bod
// Vrati sa vzdialenost
float DataStorage::GetNearestFriendDistance(const Point& target)
{
	int player = DATA_STORAGE.GetNearestFriend(target);
	return player == -1 ? 1000.0f : (world->friends[player].pos - target).GetLength();
}

// Vrati domovsku poziciu
Point DataStorage::GetHomePos()
{
#ifdef GOALIE
	return Point(-48.0, 0.0);
#else

	float s = -27.0f;
	formation.SetSide(SIDE_LEFT);
	
	//CfPoint homepos = m_cFormation.GetPlayerPosition( world->me->Unum, CfPoint( s, 0) );
    Point homepos = formation.GetPlayerPosition( world->me->GetUniform(), Point(s, 0) );
	homepos.SetX((homepos.GetX() > -2.f) ? -2.f : homepos.GetX());
	//if (world->me->GetUniform() < m_cFormation.GetPlayersCountInUnit(m_cFormation.m_nUnitDef)
	
	return homepos;
#endif
}


/*
 * Vrati formaciu
 *
 * @param formation retazec, ktoru formaciu chceme
 *
 * @return cislo formacie
 */
int DataStorage::GetFormation(const char* formation)
{
	// Asi zbytocne, v triede formation sa hodnota -1 aj tak neosetruje. Predpokladam cisto informacny/debug vyznam
    if (strncmp(formation, "Formation622", 12) == 0)
       return FORMATION_622;
    else if (strncmp(formation, "Formation532", 12) == 0)
       return FORMATION_532;
    else if (strncmp(formation, "Formation523", 12) == 0)
       return FORMATION_523;
    else if (strncmp(formation, "Formation433", 12) == 0)
       return FORMATION_433;
    else if (strncmp(formation, "Formation343", 12) == 0)
       return FORMATION_343;
    else if (strncmp(formation, "Formation334", 12) == 0)
       return FORMATION_334;

   return -1;

}

/*
 * @return vrati poziciu ofsajdovej ciary
 */
float DataStorage::GetOffsideLine()
{
	float offside_line = 0;
	float fast = 0;
	float second = 0;
	float tmp;
	int i;

	for (i = 0; i < world->enemies.GetSize(); i++)
	{	// Druhy obranca
		if (world->enemies[i].pos.GetX() > second)
		{
			second = world->enemies[i].pos.GetX();
			
			if (second > fast)
			{
				tmp = fast;
				fast = second;
				second = tmp;
			}
		}
	}

	if (second > world->ball->pos.GetX())
	{
		offside_line = second;
	}
	else
	{
		offside_line = world->ball->pos.GetX();
	}

	return offside_line;
}

/*
 * @return Je hrac v ofsajde?
 */
bool DataStorage::InOffsidePos()
{
	if (world->me->pos.GetX() <	GetOffsideLine())
		return false;	
	return true;
}

/* Nachadza sa bod v pokutovom uzemi?
 *
 * @param p bod, ktoreho polohu chceme overit
 *
 * @return ci sa dany bod nachadza v nasom pokutovom uzemi
 */
bool DataStorage::InPenaltyArea(const Point p)
{
	if(p.GetX() < world->params->flags[PLC].GetX() && fabsf(p.GetX()) < 52.5 &&
		fabsf(p.GetY()) < fabsf(world->params->flags[PLT].GetY()))
		return true;

	return false;
}

//premiestnena z GoalieHatctics.spp
int DataStorage::NumberOfEnemiesAround(const Point p,float dist) const
{
	int sum = 0;
	for(int a = 0; a < world->enemies.GetSize(); a++)
	{
		if((world->enemies[a].pos - p).GetLength() < dist)
			sum++;
	}
	return sum;
}

/*
 * return ci je mozne kopnut do lopty
 */
bool DataStorage::IsBallKickable()
{
	return ballKickable;
}

// Mam kopnut do lopty mimo modu PlayOn?
bool DataStorage::ShouldKickIn()
{
	// Pretazena metoda v PlayerTactics!
	PlayMode pm = world->state->GetPlayMode();
#ifdef GOALIE
	if(pm == PM_GoalKick_Our)
#else // GOALIE
	if(pm == PM_FreeKick_Our || pm == PM_IndFreeKick_Our || pm == PM_KickOff_Our || pm == PM_KickIn_Our ||
		pm == PM_CornerKick_Our || pm == PM_PenaltyKick_Our)
#endif // GOALIE
		return true;
	return false;
}

bool DataStorage::DependentForBall()
{
	int ourc;
	int theirc;
	int closestFriend = NearFriend(&ourc);
	int enemy = NearEnemy(&theirc);

	if(closestFriend == -1)
		return true;
	else
	{
		// Nas hrac tam bude skor
		if(enemy == -1 || theirc > ourc)
			return false;
		else
		{
			// Medzi nasim hracom a loptou je super
			if(InTheMiddle(world->enemies[enemy].pos, world->friends[closestFriend].pos, world->ball->pos))
			{
				// Najblizsi super nie je medzi mnou a loptou
				bool dependent = !InTheMiddle(world->enemies[enemy].pos, world->me->pos, world->ball->pos);
				
				// Nikto od nas nie je medzi mnou a loptou
				for (int i = 0; i < world->friends.GetSize() && dependent; i++)
				{
					if(InTheMiddle(world->friends[i].pos, world->me->pos, world->ball->pos))
						dependent = false;
				}

				return dependent;
			}
			else
				return false;
		}
	}
}

// Vrati supera najblizsieho k lopte
int DataStorage::NearEnemy(int* pCycles)
{
	if(world->enemies.GetSize() == 0)
		return -1;

	float psteps;
	Ball tmpBall(*world->ball);
	Vector diff;
	const float karea = world->params->server->player_size + world->params->server->ball_size + world->params->server->kickable_margin;

	for(int ncycles = 1; ; ncycles++)
	{
		tmpBall.Step();

		for(int i = 0; i < world->enemies.GetSize(); i++)
		{
			diff = tmpBall.pos - world->enemies[i].pos;

			psteps = (diff.GetLength() - karea) / world->params->server->player_speed_max;
			if(psteps < ncycles)
			{
				*pCycles = ncycles;
				return i;
			}
		}
	}
}

// Vrati naseho najblizsieho hraca k lopte. Vrati -1, ak som to ja.
int DataStorage::NearFriend(int* pCycles)
{
	float psteps;
	Ball tmpBall(*world->ball);
	Vector diff;

	const float mykarea = world->me->type->player_size + world->params->server->ball_size + world->me->type->kickable_margin;
	
	float kareas[12];
	float speeds[12];
	for(unsigned k = 0; k < 12; k++)
	{
		kareas[k] = world->params->server->ball_size + world->friendTypes[k]->player_size + world->friendTypes[k]->kickable_margin;
		speeds[k] = world->friendTypes[k]->player_speed_max;
	}

	for(int ncycles = 1; ; ncycles++)
	{
		tmpBall.Step();
		diff = tmpBall.pos - world->me->pos;

		psteps = (diff.GetLength() - mykarea) / world->me->type->player_speed_max;
		if(psteps < ncycles)
		{
			*pCycles = ncycles;
			return -1;
		}

		for(int i=0;i < world->friends.GetSize();i++)
		{
			diff = tmpBall.pos - world->friends[i].pos;

			psteps = (diff.GetLength() - kareas[world->friends[i].GetUniform()])/ speeds[world->friends[i].GetUniform()];
			if(psteps < ncycles)
			{
				*pCycles = ncycles;
				return i;
			}
		}
	}
}

// Smeruje lopta na nasu branku?
bool DataStorage::BallHeadingOnOurGoal(float tolerance)
{
	static float t, intersectGoalY, len, spdl;	
	static Point ourGoal;

	ourGoal = world->params->flags[GOAL_L];

	t = (ourGoal.GetX() - world->ball->pos.GetX()) / world->ball->vel.GetX();
	intersectGoalY = world->ball->pos.GetY() + world->ball->vel.GetY() * t;
	if(t > 0 && intersectGoalY > -tolerance && intersectGoalY < tolerance)
	{
		len = (world->ball->pos - Point(ourGoal.GetX(), intersectGoalY)).GetLength();
		spdl = world->ball->vel.GetLength() * world->params->server->ball_decay;
		while(len > 0 && spdl > SPEED_ON_GOAL_LINE_MIN)
		{
			len -= spdl;
			spdl *= world->params->server->ball_decay;
		}

		if(len <= 0)
			return true;
	}

	return false;
}

bool DataStorage::DangerousBall()
{
	//ak pocas 20 cyklov sa lopta ocitne v blizkosti brany (napr. sikma nahravka pred branou)
	Ball tmpBall(*world->ball);
	for(int a=0;a<=20;a++)
	{
		DBGN("x: %f, y: %f",tmpBall.pos.GetX(),tmpBall.pos.GetY());
		if(tmpBall.pos.GetX()<-47.0 && fabsf(tmpBall.pos.GetY())<7.5)
			return true;
		tmpBall.Step();
	}
	return false;
}
